package org.bhf.i18n;

/**
 * Copyright (c) 2007, Blue Hole Software. All rights reserved.
 * Code licensed under the Apache 2.0 License:
 * http://www.apache.org/licenses/
 */

import java.util.EmptyStackException;
import java.util.Locale;
import java.util.Stack;
import java.util.TimeZone;

/**
 * Thread local representation of <code>Locale</code> and <code>TimeZone</code> - collectively a
 * <code>ContextLocation</code>. In a Web/Application server environment, the user's Locale and
 * TimeZone is associated with a request rather than the container.
 */
public final class ContextLocation
{
    private final Locale locale;
    private final TimeZone timeZone;

    /**
     * Create a <code>ContextLocation with the default locale and time zone</code>.
     */
    public ContextLocation()
    {
        this( Locale.getDefault(), TimeZone.getDefault() );
    }

    /**
     * Create a <code>ContextLocation</code>.
     * @param locale Must not be <code>null</code>.
     * @param timeZone May be <code>null</code>.
     */
    public ContextLocation( final Locale locale, final TimeZone timeZone )
    {
        assert locale != null;
        this.locale     = locale;
        this.timeZone   = timeZone;
    }

    /**
     * Return the <code>Locale</code> associated with this <code>ContextLocation</code>.
     * @return the <code>Locale</code> associated with this <code>ContextLocation</code>. Will not be <code>null</code>.
     */
    public Locale                   getLocale()
    {
        return locale;
    }

    /**
     * Return the <code>TimeZone</code> associated with this <code>ContextLocation</code>.
     * @return the <code>TimeZone</code> associated with this <code>ContextLocation</code>. May be <code>null</code>.
     */
    public TimeZone                 getTimeZone()
    {
        return timeZone;
    }

    private static final ThreadLocal<Stack<ContextLocation>> localLocations = new ThreadLocal<Stack<ContextLocation>>(); // Stack of ContextLocation

    /**
     * Push a new <code>ContextLocation</code> on the thread local stack of <code>ContextLocation</code>s.
     * @param location Must not be <code>null</code>.
     */
    public static void              pushContextLocation( final ContextLocation location )
    {
        Stack<ContextLocation> locations = localLocations.get();

        if( locations == null )
        {
            locations = new Stack<ContextLocation>();
            localLocations.set( locations );
        }

        locations.push( location );
    }

    /**
     * Get the current <code>ContextLocation</code> associated with this thread.
     *
     * @return The current <code>ContextLocation</code> associated with this thread, <code>null</code> if none.
     */
    public static ContextLocation   getCurrentContextLocation()
    {
        final Stack<ContextLocation> locations = localLocations.get();
        if( locations == null )
            return null;
        try
        {
            return locations.peek();
        }
        catch( EmptyStackException ese )
        {
            return null;
        }
    }

    /**
     * Pop the current <code>ContextLocation</code> associated with this thread.
     * @throws IllegalStateException This thread does not have an associated <code>ContextLocation</code>.
     */
    public static void              popContextLocation()
        throws IllegalStateException
    {
        final Stack<ContextLocation> locations = localLocations.get();
        if( locations == null )
            throw new IllegalStateException( "locations == null" );
        locations.pop();
    }
}